﻿using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace BattleTetrixReloaded
{
    public class tet_net_client
    {
        public class SocketPacket
        {
            public System.Net.Sockets.Socket thisSocket;
            public byte[] dataBuffer = new byte[(1024 * 1024 * 4) + 100];
        }

        public Socket m_clientSocket;
        IAsyncResult m_result;
        public AsyncCallback m_pfnCallBack;
        public bool iAmConnected;

        public bool isConnected = false;
        public tet_net_object netObject = null;       

        public tet_net_client()
        { 
            netObject = new tet_net_object();
            string ip = System.IO.File.ReadAllText(@"settings.txt");
            
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress[] ipAddress = Dns.GetHostAddresses(ip);
            IPEndPoint ipEnd = new IPEndPoint(ipAddress[0], 8221);
            m_clientSocket.Connect(ipEnd);
            if (m_clientSocket.Connected)
            {                
                WaitForData();
                isConnected = true;
            }
        }

        private void WaitForData()
        {            
            if (m_pfnCallBack == null)
            {
                m_pfnCallBack = new AsyncCallback(OnDataReceived);
            }
            SocketPacket theSocPkt = new SocketPacket();
            theSocPkt.thisSocket = m_clientSocket;
            if (m_clientSocket.Connected)
                m_result = m_clientSocket.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, m_pfnCallBack, theSocPkt);           
        }

        private void OnDataReceived(IAsyncResult asyn)
        {
            SocketPacket socketData = (SocketPacket)asyn.AsyncState;

            int bytesReceived = 0;
            try
            {
                bytesReceived = socketData.thisSocket.EndReceive(asyn);
            }
            catch
            {
                tet_main.gameState = tet_constants.STATE_OP_DISC;
                return;
            }
            
            byte[] buffer = new byte[bytesReceived + 1];

            buffer = socketData.dataBuffer;

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();

            stream.Write(buffer, 0, buffer.Length);
            stream.Seek(0, 0);                

            tet_net_object objReceived;
            objReceived = (tet_net_object)formatter.Deserialize(stream);

            parseObject(objReceived);

            WaitForData();
    }

        private void parseObject(tet_net_object objReceived)
        {            
            netObject = objReceived;

            if (netObject.isGameOver == true)
            {
                tet_main.gameState = tet_constants.STATE_VS_WON;
            }
        }

        internal void sendMsgToClient(tet_net_object objToSend)
        {            
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();

            formatter.Serialize(stream, objToSend);

            byte[] buffer = ((MemoryStream)stream).ToArray();
            m_clientSocket.Send(buffer, buffer.Length, 0);            
        }

        public void Update(tet_game_loop gameLoop)
        {
            if (m_clientSocket.Connected)
            {
                tet_net_object toSend = new tet_net_object();

                if (gameLoop != null)
                {
                    toSend.ColorMatrix = gameLoop.colorMatrix;
                    toSend.ClearedLines = gameLoop.clearedLines;
                    toSend.Score = gameLoop.myScore.getScore();
                    toSend.UsedShapes = gameLoop.clearedLines;
                    toSend.NextShape = gameLoop.currentBlocks[gameLoop.usedShapesNr + 1];
                    toSend.isGameOver = gameLoop.isGameOver;
                }

                toSend.isReady = tet_main.isReady; 
                sendMsgToClient(toSend);
            }
            else
            {
                m_clientSocket.Disconnect(true); ;
                tet_main.gameState = tet_constants.STATE_OP_DISC;
            }            
        }

        public void Unload()
        {            
            m_clientSocket.Close();
        }
    }
}
